using System;
using Microsoft.SPOT;
using SecretLabs.NETMF.Hardware.Netduino;
using System.IO.Ports;
using System.Threading;

namespace Enough.Connectivity.Seeedstudio
{
    /// <summary>
    /// A connector to the BluetoothShield of Seeedstudio.
    /// You need to call Initialize() before being able to process connections.
    /// </summary>
    public class SeeedstudioBluetoothShieldConnection : IBinaryConnection    
    {
        private static SerialPort _serial;
        private static bool _isInitialized;

        private BinaryConnectionStringReceived _onStringReceived;
        public BinaryConnectionStringReceived OnStringReceived
        {
            get
            {
                return _onStringReceived;
            }
            set
            {
                _onStringReceived = value;
            }
        }


        private BinaryConnectionDataReceived _onDataReceived;
        public BinaryConnectionDataReceived OnDataReceived 
        {
            get { return _onDataReceived; }
            set { _onDataReceived = value; }
        }
       
        /// <summary>
        /// Allows to configure the Seeedstudio Bluetooth Shield for common settings
        /// </summary>
        /// <param name="serialPortname">the name of the serial port, defaults to SerialPorts.COM2</param>
        /// <param name="bluetoothName">the name of the Bluetooth shield, defaults to SeeedBTSlave</param>
        /// <param name="isMaster">defines whether the shield should be a master or a slave, defaults to false (slave)</param>
        /// <param name="allowPairedDevices">should paired devices be allowed to connect? Defaults to true.</param>
        /// <param name="allowAutoConnection">should the shield connect to the last paired device automatically? Defaults to false.</param>
        /// <param name="allowInquire">should the device allow being inquired (slave) or start inquiry (master)? Defaults to true.</param>
        /// <param name="pinCode">an optional PIN code, defaults to null (factory PIN is 0000)</param>
        public void Initialize(string serialPortname = SerialPorts.COM2, string bluetoothName = "SeeedBTSlave", bool isMaster = false, bool allowPairedDevices = true, bool allowAutoConnection = false, bool allowInquire = true, string pinCode = null)
        {
            _serial = new SerialPort(serialPortname, 38400, Parity.None, 8, StopBits.One);
            // open the serial-port, so we can send & receive data
            _serial.Open();
            // add an event-handler for handling incoming data
            _serial.DataReceived += new SerialDataReceivedEventHandler(serial_DataReceived);
            // configure seeedstudio:
            char config = isMaster ? '1' : '0';
            this.SendString("\r\n+STWMOD=" + config + "\r\n"); //set the bluetooth work in slave or master mode
            this.SendString("\r\n+STNA=" + bluetoothName + "\r\n"); //set the bluetooth name 
            config = allowPairedDevices ? '1' : '0';
            this.SendString("\r\n+STOAUT=" + config + "\r\n"); // Allow/disallow Paired device to connect me
            config = allowAutoConnection ? '1' : '0';
            this.SendString("\r\n+STAUTO=" + config + "\r\n"); // Allow/disallow connecting automatically to the last paired device
            if (pinCode != null)
            {
                this.SendString("\r\n+STPIN=" + pinCode + "\r\n"); // set the PIN
            }
            Thread.Sleep(2000); // This delay is required.
            config = allowInquire ? '1' : '0';
            this.SendString("\r\n+INQ=" + config + "\r\n"); //make the slave bluetooth inquirable
            Thread.Sleep(2000); // not sure if this delay is required.
            _isInitialized = true;
        }

       


        public void SendData(byte[] data)
        {
            _serial.Write(data, 0, data.Length);
        }

        private void serial_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            byte[] bytes = null;
            int destinationIndex = 0;
            int read = 0;
            while (_serial.BytesToRead > 0)
            {
                int expected = _serial.BytesToRead;
                byte[] data = new byte[expected];
                int currentRead = _serial.Read(data, 0, expected);
                read += currentRead;
                if (bytes == null)
                {
                    bytes = data;
                    destinationIndex = read;
                }
                else
                {
                    byte[] intermediate = new byte[read];
                    System.Array.Copy(bytes, intermediate, read - currentRead);
                    System.Array.Copy(data, 0, intermediate, destinationIndex, currentRead);
                    bytes = intermediate;
                    destinationIndex += currentRead;
                }
                if (_isInitialized)
                {
                    // give serial a chance to add more data
                    Thread.Sleep(5);
                }
            }
            if (!_isInitialized || read == 0)
            {
                // just chatter from the shield
                return;
            }
            char[] chars = new char[read];
            for (int i = 0; i < read; i++)
            {
                char c = (char)bytes[i];
                chars[i] = c;
            }
            string result = new string(chars);
            string[] lines = result.Split('\r', '\n');
            string receivedData = lines[lines.Length - 1];
            if (receivedData.Length > 1)
            {
                if ((receivedData[0] == '+') || "CONNECT:OK".Equals(receivedData))
                {
                    // that's just chatter from the shield like +BTSTATE:4
                    return;
                }
                if (_onStringReceived != null)
                {
                    _onStringReceived(this, receivedData);
                }
                if (_onDataReceived != null)
                {
                    byte[] buffer = System.Text.Encoding.UTF8.GetBytes(receivedData);
                    _onDataReceived(this, buffer);
                }
            }
        }


    }


}
